#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <cstring>
#include "InetAddr.hpp"
#include "Common.hpp"

// 对套接字的封装

namespace Socket_ns
{
    // 这里使用模板方法模式
    // 定义一些Socket使用的方法
    class Socket;
    using socket_sptr = std::shared_ptr<Socket>;
    #define BUFFER_SIZE 1024

    class Socket
    {
    public:
        // 通用接口
        virtual void CreateSocket() = 0;
        virtual void BindSocket(const InetAddr &addr) = 0;

        virtual int Recv(std::string *out) = 0;
        virtual int Send(const std::string &in) = 0;

        virtual int SockFd() = 0;

        // TCP Socket 使用的一些接口
        virtual void ListenSocket() = 0;
        // 使用智能指针对象管理服务器实例，所以返回一个智能指针对象并利用一个输出型参数得到对方的地址信息
        virtual int Accepter(InetAddr *clientAddr, int* errcode) = 0;
        virtual bool Connector(const InetAddr &serverAddr) = 0;

        virtual void Close() = 0;

        void BuildListenSocket(const InetAddr &addr)
        {
            CreateSocket();
            BindSocket(addr);
            ListenSocket();
        }

        bool BuileClientSocket(const InetAddr &addr)
        {
            CreateSocket();
            return Connector(addr);
        }
    };

    const static int g_backlog = 16;

    class TCPSocket : public Socket
    {
    public:
        TCPSocket(int sockfd =  -1) : _sockFd(sockfd)
        {}

        // 通用接口
        void CreateSocket() override
        {
            // 创建套接字信息
            _sockFd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockFd < 0)
            {
                LOG(FATAL, "server listenSock error, %s, %d", strerror(errno), errno);
                exit(SOCK_ERROR);
            }

            // 将创建socketfd设置为非阻塞
            SetNonBlock(_sockFd);

            // TCP套接字需要设置端口复用
            int opt = 1;
            ::setsockopt(_sockFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        }

        void BindSocket(const InetAddr &addr) override
        {
            // 填充网络信息
            struct sockaddr_in localAddr;
            memset(&localAddr, 0, sizeof(struct sockaddr_in));
            localAddr.sin_family = AF_INET;
            localAddr.sin_port = htons(addr.Port());
            // 这里绑定的地址可以通过用户指定
            localAddr.sin_addr.s_addr = inet_addr(addr.IP().c_str());

            // 绑定
            int n = bind(_sockFd, (struct sockaddr *)&localAddr, sizeof(struct sockaddr_in));
            if (n < 0)
            {
                LOG(FATAL, "server bind error, %s, %d", strerror(errno), errno);
                exit(BIND_ERROR);
            }
        }

        int Recv(std::string *out) override
        {
            char buffer[BUFFER_SIZE];
            ssize_t readBytes = ::recv(_sockFd, buffer, BUFFER_SIZE - 1, 0);

            if (readBytes > 0)
            {
                buffer[readBytes] = 0;
                *out += buffer; // 注意这里是将读取到的信息拼接到外部缓冲区中
                return readBytes;
            }
            else
            {
                // 读取失败 / 客户端退出，读取到文件末尾
                return readBytes;
            }
        }

        int Send(const std::string &in) override
        {
            // 将收到的消息再发送回客户端
            int sendBytes = ::send(_sockFd, in.c_str(), in.size(), 0);
            return sendBytes;
        }

        int SockFd() override
        {
            return _sockFd;
        }

        // TCP Socket 使用的一些接口
        void ListenSocket() override
        {
            // 监听，等待客户端的连接
            int n = listen(_sockFd, g_backlog);
            if (n < 0)
            {
                LOG(FATAL, "server listen error, %s, %d", strerror(errno), errno);
                exit(LISTEN_ERROR);
            }
            LOG(DEBUG, "listen success, sockfd is : %d", _sockFd);
        }

        // 返回通信fd
        int Accepter(InetAddr *clientAddr, int* errcode) override
        {
            // 获取客户端的连接 -- 等到一个用户服务的套接字
            struct sockaddr_in peer;
            socklen_t len = sizeof(struct sockaddr_in);
            int sockfd = accept(_sockFd, (struct sockaddr *)&peer, &len);
            if (sockfd < 0)
            {
                LOG(FATAL, "server accept error, %s, %d", strerror(errno), errno);
                *errcode = errno;
                return -1;
            }
            *clientAddr = peer;

            // 将创建socketfd设置为非阻塞
            SetNonBlock(sockfd);

            return sockfd;
        }

        bool Connector(const InetAddr &serverAddr) override
        {
            // 向服务器发起连接请求
            struct sockaddr_in server;
            memset(&server, 0, sizeof(struct sockaddr_in));
            server.sin_family = AF_INET;
            server.sin_port = htons(serverAddr.Port());
            // 这里不是直接设置为INADDR_ANY，由用户传入的地址设定: 服务器设置为INADDR_ANY，客户端设置服务器的地址
            server.sin_addr.s_addr = inet_addr(serverAddr.IP().c_str());

            int n = connect(_sockFd, (struct sockaddr *)&server, sizeof(struct sockaddr_in));
            if (n < 0)
            {
                LOG(WARNING, "client connect error, %s, %d", strerror(errno), errno);
                return false;
            }
            return true;
        }

        void Close()
        {
            if(_sockFd > 0)
                ::close(_sockFd);
        }

    private:
        int _sockFd;
    };
}
